// This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
//
// TagSoup is licensed under the Apache License,
// Version 2.0.  You may obtain a copy of this license at
// http://www.apache.org/licenses/LICENSE-2.0 .  You may also have
// additional legal rights not granted by this license.
//
// TagSoup is distributed in the hope that it will be useful, but
// unless required by applicable law or agreed to in writing, TagSoup
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied; not even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

package com.cbs.android.widget.fullhtmlview.tagsoup;

/**
 * The internal representation of an actual element (not an element type).
 * An Element has an element type, attributes, and a successor Element
 * for use in constructing stacks and queues of Elements.
 *
 * @see ElementType
 * @see AttributesImpl
 */
public class Element {


    private ElementType theType;        // type of element
    private AttributesImpl theAtts;        // attributes of element
    private Element theNext;        // successor of element
    private boolean preclosed;        // this element has been preclosed

    /**
     * Return an Element from a specified ElementType.
     *
     * @param type              The element type of the newly constructed element
     * @param defaultAttributes True if default attributes are wanted
     */

    public Element(ElementType type, boolean defaultAttributes) {
        theType = type;
        if (defaultAttributes) theAtts = new AttributesImpl(type.atts());
        else theAtts = new AttributesImpl();
        theNext = null;
        preclosed = false;
    }

    /**
     * Return the element type.
     *
     * @return The element type.
     */

    public ElementType type() {
        return theType;
    }

    /**
     * Return the attributes as an AttributesImpl object.
     * Returning an AttributesImpl makes the attributes mutable.
     *
     * @return The attributes
     * @see AttributesImpl
     */
    public AttributesImpl atts() {
        return theAtts;
    }

    /**
     * Return the next element in an element stack or queue.
     *
     * @return The next element
     */

    public Element next() {
        return theNext;
    }

    /**
     * Change the next element in an element stack or queue.
     *
     * @param next The new next element
     */

    public void setNext(Element next) {
        theNext = next;
    }

    /**
     * Return the name of the element's type.
     * Convenience method.
     *
     * @return The element type name
     */

    public String name() {
        return theType.name();
    }

    /**
     * Return the namespace name of the element's type.
     * Convenience method.
     *
     * @return The element type namespace name
     */

    public String namespace() {
        return theType.namespace();
    }

    /**
     * Return the local name of the element's type.
     * Convenience method.
     *
     * @return The element type local name
     */

    public String localName() {
        return theType.localName();
    }

    /**
     * Return the content model vector of the element's type.
     * Convenience method.
     *
     * @return The content model vector
     */

    public int model() {
        return theType.model();
    }

    /**
     * Return the member-of vector of the element's type.
     * Convenience method.
     *
     * @return The member-of vector
     */

    public int memberOf() {
        return theType.memberOf();
    }

    /**
     * Return the flags vector of the element's type.
     * Convenience method.
     *
     * @return The flags vector
     */

    public int flags() {
        return theType.flags();
    }

    /**
     * Return the parent element type of the element's type.
     * Convenience method.
     *
     * @return The parent element type
     */

    public ElementType parent() {
        return theType.parent();
    }

    /**
     * Return true if the type of this element can contain the type of
     * another element.
     * Convenience method.
     *
     * @param other The other element
     */

    public boolean canContain(Element other) {
        return theType.canContain(other.theType);
    }


    /**
     * Set an attribute and its value into this element.
     *
     * @param name  The attribute name (Qname)
     * @param type  The attribute type
     * @param value The attribute value
     */

    public void setAttribute(String name, String type, String value) {
        theType.setAttribute(theAtts, name, type, value);
    }

    /**
     * Make this element anonymous.
     * Remove any <tt>id</tt> or <tt>name</tt> attribute present
     * in the element's attributes.
     */

    public void anonymize() {
        for (int i = theAtts.getLength() - 1; i >= 0; i--) {
            if (theAtts.getType(i).equals("ID") ||
                    theAtts.getQName(i).equals("name")) {
                theAtts.removeAttribute(i);
            }
        }
    }

    /**
     * Clean the attributes of this element.
     * Attributes with null name (the name was ill-formed)
     * or null value (the attribute was present in the element type but
     * not in this actual element) are removed.
     */

    public void clean() {
        for (int i = theAtts.getLength() - 1; i >= 0; i--) {
            String name = theAtts.getLocalName(i);
            if (theAtts.getValue(i) == null || name == null ||
                    name.length() == 0) {
                theAtts.removeAttribute(i);
                continue;
            }
        }
    }

    /**
     * Force this element to preclosed status, meaning that an end-tag has
     * been seen but the element cannot yet be closed for structural reasons.
     */

    public void preclose() {
        preclosed = true;
    }

    /**
     * Return true if this element has been preclosed.
     */

    public boolean isPreclosed() {
        return preclosed;
    }

}
